﻿#define  _CRT_SECURE_NO_WARNINGS




// 多态中虚函数表的相关概念，多继承中的多态虚函数表，动态绑定与静态绑定，菱形虚继承中的多态虚函数表




#include <iostream>
using namespace std;




// 多态
// 
// 
// 虚函数表（虚表）：本质函数指针数组 
// 
// 
// 无论父类或子类只要一个自定义类声明中出现了virtual虚函数
// 那么这个类的实例化对象就会产生一个对应的虚函数列表，用来保存虚函数所在的地址
// 
// 即使子类声明中不出现virtual虚函数对父类的虚函数进行重写，但是子类也会产生一个虚函数表，并且为直接复制父类的虚函数表
// 
//
//class Base
//{
//public:
//	Base()// 构造函数中有this指针但不需要显示的传递
//	{
//		++_b;
//	}
//
//
//	virtual void Func1()
//	{
//		cout << "Base::Func1()" << endl;
//	}
//	virtual void Func2()
//	{
//		cout << "Base::Func2()" << endl;
//	}
//	void Func3()
//	{
//		cout << "Base::Func3()" << endl;
//	}
//private:
//	int _b = 1;
//};
//
//class Derive : public Base
//{
//public:
//	virtual void Func1()
//	{
//		cout << "Derive::Func1()" << endl;
//	}
//	virtual void Func4()
//	{
//		cout << "Derive::Func4 ()" << endl;
//	}
//private:
//	int _d = 2;
//};

// 使用程序打印虚表
// 
// typedef函数指针类型void(*)() 重命名为 VF_PTR
//typedef void(*VF_PTR)();

//void PrintVFTable(VF_PTR table[])// 传入函数指针数组
//void PrintVFTable(VF_PTR* table)// 传入函数指针数组
//{
//	for(int i = 0; table[i] != nullptr; ++i)
//	{
//		printf("[%d]:%p->", i, table[i]);
//		
//		// 把虚标中的每个虚函数都调用一遍
//		VF_PTR f = table[i];// 函数名就是地址
//		f();// 通过函数指针调用函数
//	}
//	cout << endl;
//}

// 输出结果：
// 
// [0]:00621091
// [1]:00621384
// 
// [0]:0062140B
// [1]:00621384
// [2]:00621073  ---  父类没有的，子类自己新写的虚函数，进入子类自己的虚表中
// 
// 
// [0]:00B6108C->Base::Func1()
// [1] : 00B61384->Base::Func2()
//
// [0] : 00B6140B->Derive::Func1()
// [1] : 00B61384->Base::Func2()
// [2] : 00B6106E->Derive::Func4()


//int main()
//{
//	Base b;
//	Derive d;
//	
//	// 取出虚表数组的首地址，传给PrintVFTable
//	//PrintVFTable((VF_PTR*)(*(int*)&b));
//	//PrintVFTable((VF_PTR*)(*(int*)&d));
//	// 局限性：由于强转为int*仅能在32位的情况下跑通
//	// 
//	// 虚表的首地址存储在对象的前4B所以强制转换为int*取得虚表的首地址，再通过解引用取出该指针指向的内容为VF_PTR
//	// 此时该地址被识别为int需要传参给VF_PTR table[] 函数指针数组，（无法通过隐式类型转换直接传入，需要强制类型转换）
//	// 再进行强制类型转换为VF_PTR* 一个函数指针的指针充当函数指针数组的首地址 
//	PrintVFTable(*(VF_PTR**)&b);
//	PrintVFTable(*(VF_PTR**)&d);
//	// 目标需要VF_PTR*，需要取出&b的前4B，强转成VF_PTR**，进行解引用，取得VF_PTR*，传入PrintVFTable
//
//	return 0;
//}


// 通过观察和测试，我们发现了以下几点问题：
// 
// 
// 
// 1. 派生类对象d中也有一个虚表指针，d对象由两部分构成
// 一部分是父类继承下来的成员，虚表指针也就是存在部分的另一部分是自己的成员。
//
// 2. 基类b对象和派生类d对象虚表是不一样的，这里我们发现Func1完成了重写，所以d的虚表
// 中存的是重写的Derive::Func1，所以虚函数的重写也叫作覆盖，覆盖就是指虚表中虚函数的覆盖。
// 重写是语法的叫法，覆盖是原理层的叫法。
//
// 3. 另外Func2继承下来后是虚函数，所以放进了虚表，Func3也继承下来了，但是不是虚函数，所以不会放进虚表。
//
// 4. 虚函数表本质是一个存虚函数指针的指针数组，一般情况这个数组最后面放了一个nullptr。（vs中存在，g++不存储在）
//
// 5. 总结一下派生类的虚表生成：
//							a.先将基类中的虚表内容拷贝一份到派生类虚表中 
//							b.如果派生类重写了基类中某个虚函数，用派生类自己的虚函数覆盖虚表中基类的虚函数 
//							c.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。
// 
// 
// 6. 这里还有一个童鞋们很容易混淆的问题：虚函数存在哪的？虚表存在哪的？ 
// 注意：
// 虚表存的是虚函数指针，不是虚函数，虚函数和普通函数一样的，都是存在代码段的，
// 只是他的指针又存到了虚表中。
// 另外对象中存的不是虚表，存的是虚表指针。
// 那么虚表存在哪的呢？实际我们去验证一下会发现vs下是存在代码段的，Linux g++下大家自己去验证？
// 
// 
// 
// 
// 
// 
// 
// 
// 
// 
// 
// 
// 重点三问：
// 
// 1.虚函数列表什么阶段生成的？  --  编译
// 
// 2.实例化对象中的虚表指针_vfptr什么时候初始化的？  --  构造函数的初始化列表
// 
// 3.虚表存储在哪里？  --  代码段（常量区）  ---  类的虚表在编译完成后就不会再修改了
//						堆
//						栈
//						数据段（静态区）
//						代码段（常量区）   ---  编译好的函数也在代码段
// 
// 
// 
// 
//int main()
//{
//	Base b;
//	Derive d;
//
//	int x = 0;// 栈
//	static int y = 0;// 数据段（静态区）
//	int* z = new int;// 堆
//	const char* p = "XXXXXXX";// 代码段（常量区）
//
//	printf("栈对象:%p\n", &x);
//	printf("堆对象:%p\n", z);
//	printf("数据段（静态区）对象:%p\n", &y);
//	printf("代码段（常量区）对象:%p\n", p);
//	printf("b对象虚表:%p\n", *((void**)&b));
//	printf("d对象虚表:%p\n", *((void**)&d));
//
//
//// 虚表在代码段（常量区）存储
//// 
//// 栈对象:003AF7B0
//// 堆对象 : 00926300
//// 数据段（静态区）对象 : 003CD414
//// 代码段（常量区）对象 : 003CABA8
//// b对象虚表 : 003CAB34
//// d对象虚表 : 003CAB64
//
//	return 0;
//}
// 
// 
// 

















// 多继承中的多态虚函数表
// 
// 
// 
// 
class Base1 {
public:
	virtual void func1() { cout << "Base1::func1" << endl; }
	virtual void func2() { cout << "Base1::func2" << endl; }
private:
	int b1;
};
class Base2 {
public:
	virtual void func1() { cout << "Base2::func1" << endl; }
	virtual void func2() { cout << "Base2::func2" << endl; }
private:
	int b2;
};
class Derive : public Base1, public Base2 {
public:
	virtual void func1() { cout << "Derive::func1" << endl; }
	virtual void func3() { cout << "Derive::func3" << endl; }
private:
	int d1;
};

// 使用程序打印虚标
// 
// typedef函数指针类型void(*)() 重命名为 VF_PTR
typedef void(*VF_PTR)();

void PrintVFTable(VF_PTR* table)// 传入函数指针数组
{
	for(int i = 0; table[i] != nullptr; ++i)
	{
		printf("[%d]:%p->", i, table[i]);
		
		// 把虚标中的每个虚函数都调用一遍
		VF_PTR f = table[i];// 函数名就是地址
		f();
	}
	cout << endl;
}

//int main()
//{
//	//Base1 b1;
//	//Base2 b2;
//
//	//Derive d;
//	
//
//	//PrintVFTable(*(VF_PTR**)&b1);
//	//PrintVFTable(*(VF_PTR**)&b2);
//
//	//PrintVFTable(*(VF_PTR**)&d);
//	//// 如何得到Derive类对象 对应Base2虚表的首地址
//	//// 1.
//	//PrintVFTable(*(VF_PTR**)((char*)&d + sizeof(Base1)));
//	//// 2.
//	//Base2* ptr2 = &d;
//	//PrintVFTable(*(VF_PTR**)ptr2);
//	//// 3.
//	//PrintVFTable(*(VF_PTR**)(Base2*)&d);
//
//
//	// Derive 对象中有两个虚表，分别是赋值Base1和Base2的 并对func1进行重写（会对两张虚表都进行重写）
//	// [0] : 00741244->Derive::func1 
//	// [0] : 00741352->Derive::func1
//	// 
//	// 多继承以后，两张重写func1的虚表中，func1的存储地址不同为什么？
//	// 
//	// 第一个继承的父类Base1虚表中重写的func1仅需要一次jmp即可直接访问到重写的func1
//	// 第二个继承的父类Base2虚表中重写的func1需要经过多次地址的jmp，才能访问到重写的func1
//	// 
//	// 第二个继承的父类指针访问子类中重写的虚函数，需要修正this指针所指向的位置，保持this指向子类的首地址
//	// 
//	
//	Derive d;
//	Base1* ptr1 = &d;
//	Base2* ptr2 = &d;
//
//	ptr1->func1();
//	ptr2->func1();
//
//	
//	// 
//	// 
//	// 
//	// 
//	// 输出：
//	// 
//	// Base1:
//	// [0]:007411B8->Base1::func1
//	// [1]:007412E4->Base1::func2
//	// 
//	// Base2:
//	// [0] : 007413B6->Base2::func1
//	// [1] : 007410B9->Base2::func2
//	// 
//	// Derive:
//	//		Base1多对应的
//	// [0] : 00741244->Derive::func1 
//	// [1] : 007412E4->Base1::func2
//	// [2] : 00741230->Derive::func3   ----  子类中新增加的virtual虚函数放到第一个继承类所对应的虚表后面
//	//		Base2所对应的
//	// [0] : 00741352->Derive::func1
//	// [1] : 007410B9->Base2::func2
//	// 
//	//
//
//	return 0;
//}







// 
// 
//class Base1 { public:int _b1; };
//class Base2 { public:int _b2; };
//class Derive : public Base1, public Base2 { public:int _d; };

//int main() 
//{
//	Derive d;
//	Base1* p1 = &d;
//	Base2* p2 = &d;
//	Derive* p3 = &d;
//
//	return 0;
//}
// 谁先继承 - 谁先声明 - 谁先定义 - 谁排在内存前面
// 
// 输出：
// p1 == p3 != p2
// 










// 动态绑定与静态绑定
// 
// 
// 静态多态（编译时）
// 1. 静态绑定又称为前期绑定(早绑定)，在程序编译期间确定了程序的行为，也称为静态多态，比如：函数重载
// 
// 动态多态（运行时）
// 2. 动态绑定又称后期绑定(晚绑定)，是在程序运行期间，根据具体拿到的类型确定程序的具体行为，调用具体的函数，也称为动态多态。
// 运行时才在虚表中找对应需要调用函数的地址
// 
// 











// 菱形虚继承中的多态虚函数表
// 
// 
// 
// 
class A
{
public:
	virtual void func1()
	{

	}

public:
	int _a;
};

class B : virtual public A
{
public:
	virtual void func1()
	{

	}
	virtual void func2()
	{

	}
public:
	int _b;
};

class C : virtual public A
{
public:
	virtual void func1()
	{

	}
	virtual void func3()
	{

	}
public:
	int _c;
};
class D : public B, public C 
{
public:
	virtual void func1()
	{

	}

public:
	int _d;
};


// D类声明中必须包含有虚函数，否则会报错，
// 原因：D整个类的虚表地址存储在A类所在的区域中，并采用D类最后重写覆盖的虚函数为准


int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	return 0;
}

// 整个D类的存储结构
// 
// 0x001CF950  b8 9b a1 00 ? ? ? .   ---  虚表指针（存储虚函数地址）  ----   A类没有，B类单独新增的虚函数，并且可以被D覆盖
// 0x001CF954  68 9b a1 00  h ? ? .  ---  虚基表指针（存储偏移量找A的基类，或者找该类对应的虚表地址的偏移量）
// 0x001CF958  03 00 00 00  ....
// 
// 0x001CF95C  4c 9b a1 00  L ? ? .  ---  虚表指针  ----   A类没有，C类单独新增的虚函数，并且可以被D覆盖
// 0x001CF960  78 9b a1 00  x ? ? .  ---  虚基表指针
// 0x001CF964  04 00 00 00  ....
// 
// 0x001CF968  05 00 00 00  ....
// 
// 0x001CF96C  5c 9b a1 00  \ ? ? .  ---  虚表指针  ----  A类的虚表指针，必须有被D覆盖/重写 
// 0x001CF970  02 00 00 00  ....
// 0x001CF974  cc cc cc cc ? ? ? ?
// 
// 
// 
// 
// 
// 若BC有新增的虚函数，是否可放入A所在的虚表中?
// 不放
// 
//